﻿using System.Dynamic;
using System.Linq;
using System.Linq.Expressions;
using System.Configuration;
using System.Resources;
using System.Reflection;
using System.ComponentModel.DataAnnotations;
using System.Collections.Generic;
using System.Text;
using System.Web.Mvc.Html;
using Whoever.Framework.Enum;
using Whoever.Framework.Base;
using Whoever.Framework.Utils;
using Whoever.Framework.Attributes;

using Whoever.Patterns.MVC.Models;
using Whoever.Patterns.MVC.Utils;

namespace System.Web.Mvc
{
    public static class HtmlHelperExtensions
    {
        public static MvcHtmlString LocalizedLabelFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression)
        {
            var displayName = string.Empty;

            var memberExpression = (MemberExpression)expression.Body;
            var entityName = memberExpression.Member.DeclaringType.Name;
            var propertyName = memberExpression.Member.Name;

            var resourceBaseName = ConfigurationManager.AppSettings["DisplayNameResourceBaseName"];
            var resourceAssembly = ConfigurationManager.AppSettings["DisplayNameResourceAssembly"];
            if (!string.IsNullOrEmpty(resourceBaseName) && !string.IsNullOrEmpty(resourceAssembly))
            {
                var assembly = Assembly.Load(resourceAssembly);
                var resourceManager = new ResourceManager(resourceBaseName, assembly);
                displayName = resourceManager.GetString(string.Format("{0}.{1}", entityName, propertyName));
            }

            if (string.IsNullOrEmpty(displayName))
            {
                DisplayAttribute displayAttribute = (DisplayAttribute)memberExpression.Member.GetCustomAttributes(typeof(DisplayAttribute), true).FirstOrDefault();
                if (displayAttribute != null)
                    displayName = displayAttribute.Name;
                else
                    displayName = propertyName;
            }

            return new MvcHtmlString(string.Format("<label for='{0}'>{1}</label>", propertyName, displayName));
        }

        public static MvcHtmlString Image<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper,
            Expression<Func<TModel, TProperty>> expression, int? width = null, int? height = null, string alt = null, string classAttribute = null)
        {
            var memberExpression = (MemberExpression)expression.Body;
            var propertyName = memberExpression.Member.Name;

            var dimensions = height != null && width != null ? string.Format("height=\"{0}\" width=\"{1}\"", height, width) : string.Empty;
            var altText = alt != null ? string.Format("alt=\"{0}\"", alt) : string.Empty;
            var css = classAttribute != null ? string.Format("class=\"{0}\"", classAttribute) : string.Empty;

            return new MvcHtmlString(string.Format("<img data-bind=\"attr:{{src:{0} }}\" {1} {2} {3} //>", propertyName, dimensions, altText, css));
        }

        public static MvcHtmlString KOTextBoxFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper,
            Expression<Func<TModel, TProperty>> expression)
        {
            var memberExpression = (MemberExpression)expression.Body;
            var propertyInfo = (PropertyInfo)memberExpression.Member;

            var binding = KO.BindingByType(propertyInfo.PropertyType, true);

            var htmlAttributes = new Dictionary<string, object>
            {
                {"data-bind", string.Format("{0}: {1}",binding, propertyInfo.Name)},
                {"class", "form-control"}
            };

            return htmlHelper.HTML5TextBoxFor(expression, htmlAttributes);
        }

        public static MvcHtmlString HTML5TextBoxFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper,
            Expression<Func<TModel, TProperty>> expression, IDictionary<string, object> htmlAttributes)
        {
            var memberExpression = (MemberExpression)expression.Body;
            var property = memberExpression.Member;

            htmlAttributes = HTML5.GetAttributesFromDataAnnotations(property, htmlAttributes);

            return htmlHelper.TextBoxFor(expression, htmlAttributes);
        }

        public static MvcHtmlString KOLocalizedSelectFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, IEnumerable<BusinessEntity> list, string optionsText, string optionsValue, string value)
        {
            var memberExpression = (MemberExpression)expression.Body;
            var propertyName = memberExpression.Member.Name;
            var isRequired = memberExpression.Member.GetCustomAttributes(typeof(RequiredAttribute), true).Length != 0;

            var hasItem = list != null && list.Count() > 0;

            return
                new MvcHtmlString(
                    string.Format(
                        "<select data-bind=\"options: $parent.{0}, optionsText: '{1}',optionsValue: '{2}',validValue: {3}, optionsCaption: '-Select a item-' \" {4} {5}></select>",
                        propertyName, optionsText, optionsValue, value, hasItem ? "" : "disabled='disabled'",
                        isRequired ? "required='required'" : string.Empty));
        }

        public static MvcHtmlString LocalizedAction(this HtmlHelper htmlHelper, string actionName)
        {
            if (string.IsNullOrEmpty(actionName))
                throw new Exception("The actionName parameter is empty");

            var actionDescription = string.Empty;

            var resourceBaseName = ConfigurationManager.AppSettings["ActionResourceBaseName"];
            var resourceAssembly = ConfigurationManager.AppSettings["ActionResourceAssembly"];

            if (!string.IsNullOrEmpty(resourceBaseName) && !string.IsNullOrEmpty(resourceAssembly))
            {
                var assembly = Assembly.Load(resourceAssembly);
                var resourceManager = new ResourceManager(resourceBaseName, assembly);
                actionDescription = resourceManager.GetString(actionName);
            }

            if (string.IsNullOrEmpty(actionDescription))
                actionDescription = actionName;

            return new MvcHtmlString(actionDescription);
        }

        public static MvcHtmlString LocalizedText(this HtmlHelper htmlHelper, string textName)
        {
            if (string.IsNullOrEmpty(textName))
                throw new Exception("The textName parameter is empty");

            var textDescription = string.Empty;

            var resourceBaseName = ConfigurationManager.AppSettings["TextResourceBaseName"];
            var resourceAssembly = ConfigurationManager.AppSettings["TextResourceAssembly"];

            if (!string.IsNullOrEmpty(resourceBaseName) && !string.IsNullOrEmpty(resourceAssembly))
            {
                var assembly = Assembly.Load(resourceAssembly);
                var resourceManager = new ResourceManager(resourceBaseName, assembly);
                textDescription = resourceManager.GetString(textName);
            }

            if (string.IsNullOrEmpty(textDescription))
                textDescription = textName;

            return new MvcHtmlString(textDescription);
        }

        public static MvcHtmlString LocalizedMessage(this HtmlHelper htmlHelper, string messageName, MessageType messageType)
        {
            if (string.IsNullOrEmpty(messageName))
                throw new Exception("The messageName parameter is empty");

            var textDescription = string.Empty;

            var resourceBaseName = ConfigurationManager.AppSettings["MessageResourceBaseName"];
            var resourceAssembly = ConfigurationManager.AppSettings["MessageResourceAssembly"];

            if (!string.IsNullOrEmpty(resourceBaseName) && !string.IsNullOrEmpty(resourceAssembly))
            {
                var assembly = Assembly.Load(resourceAssembly);
                var resourceManager = new ResourceManager(resourceBaseName, assembly);
                textDescription = resourceManager.GetString(string.Format("{0}.{1}", messageType.ToString(), messageName));
            }

            if (string.IsNullOrEmpty(textDescription))
                textDescription = string.Format("{0}.{1}", messageType.ToString(), messageName);

            return new MvcHtmlString(textDescription);
        }

        public static MvcHtmlString KOGridTemplate<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, params GridAction[] actions)
        {
            var htmlBuilder = new StringBuilder();
            var elementType = expression.ReturnType.GetGenericArguments()[0];
            string keyName = ReflectionHelper.GetKeyName(elementType);
            htmlBuilder.AppendLine(string.Format("<script type='text/html' id='{0}grid' name='{0}grid'>", elementType.Name));
            //htmlBuilder.AppendLine("<pre data-bind='text: ko.computed(function() { debugger; })'></pre>");
            htmlBuilder.AppendLine("    <tr>");

            foreach (var property in elementType.GetProperties())
                if (ReflectionHelper.ContainAttribute(property, typeof(GridDisplayable)))
                {
                    htmlBuilder.AppendLine("        <td>");
                    htmlBuilder.AppendLine(string.Format("          <span data-bind=\"text: {0}()\"></span>", property.Name));
                    htmlBuilder.AppendLine("        </td>");
                }

            htmlBuilder.AppendLine("        <td class='grid-actions text-right'>");
            htmlBuilder.AppendLine(string.Format("          <span class='glyphicon glyphicon-zoom-in' data-bind='click: $root.SelectItem.bind($data, $root.Controller + \"Read\")'></span> </a>"));
            htmlBuilder.AppendLine(string.Format("          <span class='glyphicon glyphicon-pencil' data-bind='click: $root.SelectItem.bind($data, $root.Controller + \"Update\")'></span> </a>"));
            htmlBuilder.AppendLine(string.Format("          <span class='glyphicon glyphicon-trash' data-bind='click: $root.SelectItem.bind($data, $root.Controller + \"Delete\")'></span> </a>"));

            foreach (var action in actions)
                htmlBuilder.AppendLine(string.Format("          <span class='{0}' data-bind='click: $root.SelectItem.bind($data, \"{1}\") {2}'>{3}</span> </a>",
                    action.CssClass, action.ActionName, !string.IsNullOrEmpty(action.KOVisible) ? string.Format(", visible: {0}", action.KOVisible) : string.Empty, action.Text));

            htmlBuilder.AppendLine("        </td>");
            htmlBuilder.AppendLine("    </tr>");
            htmlBuilder.AppendLine("</script>");

            return new MvcHtmlString(htmlBuilder.ToString());
        }

        public static MvcHtmlString KOTemplateForEach<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, string arrayName = null, string templateClass = null)
        {
            var defaultArrayName = "List";
            var defaultTemplateClass = "grid";

            var htmlBuilder = new StringBuilder();
            var elementType = expression.ReturnType.GetGenericArguments()[0];

            arrayName = arrayName ?? defaultArrayName;
            templateClass = templateClass ?? defaultTemplateClass;

            var templateName = string.Format("{0}{1}", elementType.Name, templateClass);

            if (templateClass == defaultTemplateClass)
            {
                htmlBuilder.AppendLine("<div class='table-responsive'>");
                htmlBuilder.AppendLine(string.Format("  <table class='table {0} border-indigo'>", defaultTemplateClass));
                htmlBuilder.AppendLine("        <thead>");
                htmlBuilder.AppendLine("            <tr>");

                foreach (var property in elementType.GetProperties())
                {
                    if (ReflectionHelper.ContainAttribute(property, typeof(GridDisplayable)))
                    {
                        htmlBuilder.AppendLine("                <th>");
                        htmlBuilder.AppendLine(string.Format("                  <b><span>{0}</span> </b>", property.Name));
                        htmlBuilder.AppendLine("                </th>");
                    }
                }
                htmlBuilder.AppendLine("                <th class='text-right'>");
                htmlBuilder.AppendLine(string.Format("                  <span text='{0}'>{0}</span>", Localized.Text("Actions")));
                htmlBuilder.AppendLine("                </th>");

                htmlBuilder.AppendLine("            </tr>");
                htmlBuilder.AppendLine("        </thead>");
            }

            htmlBuilder.AppendLine("                <tbody data-bind=\"template: {name: '" + templateName + "', foreach: $parent." + arrayName + "}\"'></tbody>");

            if (templateClass == defaultTemplateClass)
            {
                htmlBuilder.AppendLine("    </table>");
                htmlBuilder.AppendLine("</div>");
            }

            return new MvcHtmlString(htmlBuilder.ToString());
        }
    }
}
